
// draws 3 graphs for 3 different stonks
// uses pladv, plvpor, and plwind

#include <Python.h>
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <numpy/arrayobject.h>
#include <plplot/plplot.h>

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>

void get_user_input(char *input) {
    printf("Enter a ticker: ");
    if (fgets(input, sizeof(input), stdin) != NULL) {
        input[strcspn(input, "\n")] = '\0'; 

    } else {
          perror("Error reading input");
    }
    size_t len = strlen(input);
    // turn to caps for aesthetics.
    for (unsigned int i=0; i < len; i++) {
        input[i] = toupper(input[i]);
    }
    return;
}

PyObject *get_from_python(PyObject *fn, const char *ticker) {
    if (fn == NULL) {
        return NULL;
    }
    PyObject *pargs = PyTuple_Pack(
        1, PyUnicode_FromString(ticker)
    ); 
    PyObject *mkt_prices = PyObject_CallObject(fn, pargs);
    if (mkt_prices == NULL) {
        if (PyErr_Occurred()) {
            PyErr_Print();
        }
        fprintf(stderr, "call to invoke_from_c() failed\n");
        goto free;
    }
    if (!PyArray_Check(mkt_prices)) {
        fprintf(stderr, "did not get an array");
        Py_XDECREF(mkt_prices);
        mkt_prices = NULL;
    }
    free:
    if (pargs) {
        Py_XDECREF(pargs);
    }
    // printf("%x\n", mkt_prices);
    return mkt_prices;
}

struct MarketData {
    unsigned int row_count;
    double xmin;
    double xmax;
    double ymin;
    double ymax;
    double last_price;
    double *x;
    double *y;
};

bool get_market_data(
    const PyObject *dataframe, struct MarketData *m) {
    if (dataframe == NULL) {
        return false;
    }
    if (!PyArray_Check(dataframe)) {
        return false;
    }
    int nd = PyArray_NDIM(
        (PyArrayObject *)dataframe
    );
    npy_intp *dims = PyArray_DIMS(
        (PyArrayObject *)dataframe
    );
    m -> x = NULL;
    m -> y = NULL;
    m -> row_count = dims[0];
    if (m -> row_count > 0) {
        m -> x = (double*) malloc (
            m -> row_count * sizeof(double));
        m -> y = (double*) malloc (
            m -> row_count * sizeof(double));   
    } else {
        return false;
    }
    m -> ymin = -1;
    double time, close, high, low;
    //  time,   open,   high,   low,    close,  volume
    //  0       1       2       3       4       5
    for (npy_intp i = 0; i < m -> row_count; i++) {
        time = *(double *)PyArray_GETPTR2(
            (PyArrayObject *)dataframe, i, 0);
        close = *(double *)PyArray_GETPTR2(
            (PyArrayObject *)dataframe, i, 4);
        high = *(double *)PyArray_GETPTR2(
            (PyArrayObject *)dataframe, i, 2);
        low = *(double *)PyArray_GETPTR2(
            (PyArrayObject *)dataframe, i, 3);
        m -> x[i] = time;
        m -> y[i] = close;
        if (high > m -> ymax) {
            m -> ymax = high;
        }
        if (low < m -> ymin || m -> ymin < 0) {
            m -> ymin = low;
        }
    }
    m -> xmin = *(double *)PyArray_GETPTR2(
        (PyArrayObject *)dataframe, 0, 0);
    m -> xmax = *(double *)PyArray_GETPTR2(
        (PyArrayObject *)dataframe, (m -> row_count - 1), 0);
    return true;
}

// https://plplot.sourceforge.net/docbook-manual/plplot-html-5.15.0/plbox.html
int main() {
    //tickers
    #define TICKER_COUNT 3
    // tickers
    char bufs[TICKER_COUNT][10];
    printf("The program will prompt for 3 tickers.\n");
    for (unsigned int i = 0; i < TICKER_COUNT; i++) {
        get_user_input(bufs[i]);
        printf("echo: %s\n", bufs[i]);
    }

    Py_Initialize();
    import_array1(0); 
    PyObject * const SCRIPT_NAME 
        = PyUnicode_FromString("demo1_get-price");
    PyObject * const SCRIPT 
        = PyImport_Import(SCRIPT_NAME);
    if (SCRIPT == NULL) {
        PyErr_Print();
        fprintf(stderr, "Failed to load 'demo1'\n");
        goto end;
    }
    PyObject * const PYFN 
        = PyObject_GetAttrString(SCRIPT, "invoke_from_c");
    if (PYFN == NULL || !PyCallable_Check(PYFN)) {
        if (PyErr_Occurred()) {
            PyErr_Print();
        }
        goto end;
    }
    // invoke before plinit so a graph immediately appears.
    PyObject *dataframe = get_from_python(PYFN, bufs[0]);
    if (dataframe == NULL) {
        if (PyErr_Occurred()) {
            PyErr_Print();
        }
        goto end;
    }
    // end of python stuff.
    struct MarketData graph_params;
    char title[24];
    double current_price;
    bool is_ok = true;
    plinit(); 

    // in the tk device, this loop advances after
    // hitting the >> button
    for (unsigned int i = 0; i < (TICKER_COUNT); i++) {
        // concat strings for title of the graph
        // p = get_current_price(dataframe);
        is_ok = get_market_data(
            dataframe, &graph_params);
        if (!is_ok) {
            break;
        }
        current_price 
            = graph_params.y [graph_params.row_count - 1];
        sprintf(
            title, "%s @ %.2f (%i)", bufs[i], current_price, i);
        // set up view port without plenv()
        // note to self, don't invoke plenv() with pladv()
        pladv(0);
        plvpor(0.15, 0.90, 0.15, 0.9); 
        plwind(
            graph_params.xmin,
            graph_params.xmax,
            graph_params.ymin,
            graph_params.ymax
        ); 
        // b = draw bottom or left border
        // t = draw ticks
        // n = label major ticks
        // 0 = auto
        plbox("btn", 0, 0, "btn", 0, 0);
        pllab( "x in unix time", "Price (USD)", title);
        plline(
            graph_params.row_count, 
            graph_params.x, 
            graph_params.y );
        plflush();
        if (i+1 < TICKER_COUNT) {
            dataframe = get_from_python(PYFN, bufs[i+1]);
        }
        if (dataframe == NULL) {
            break;
        }
        if (graph_params.x) free(graph_params.x);
        if (graph_params.y) free(graph_params.y);
    }

    plend();
    // if (x) free(x);
    // if (y) free(y);
    if (dataframe) {
    // printf("%x\n", dataframe);
        Py_XDECREF(dataframe);
    }
    end:
    if (PYFN) {
        Py_XDECREF(PYFN);
    }
    if (SCRIPT) {
        Py_DECREF(SCRIPT);
    }
    if (SCRIPT_NAME) {
        Py_DECREF(SCRIPT_NAME);
    }
    Py_Finalize();
    return 0;
}

